package com.bestcem.xm.qdes.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bestcem.bp.xm.user.grpc.v1.services.User;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.component.mybatis.service.impl.XmServiceImpl;
import com.bestcem.xm.qdes.controller.param.*;
import com.bestcem.xm.qdes.controller.vo.FolderVo;
import com.bestcem.xm.qdes.controller.vo.ProjectFolderChildVo;
import com.bestcem.xm.qdes.controller.vo.ProjectLabelVo;
import com.bestcem.xm.qdes.controller.vo.QdesFolderCountVo;
import com.bestcem.xm.qdes.entity.QdesProjectEntry;
import com.bestcem.xm.qdes.entity.QdesProjectFolder;
import com.bestcem.xm.qdes.entity.QdesTagRef;
import com.bestcem.xm.qdes.grpc.client.UserGrpcClient;
import com.bestcem.xm.qdes.mapper.QdesProjectEntryMapper;
import com.bestcem.xm.qdes.mapper.QdesProjectFolderMapper;
import com.bestcem.xm.qdes.mapper.QdesTagRefMapper;
import com.bestcem.xm.qdes.service.QdesProjectFolderService;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.bestcem.xm.qdes.enums.ProjectBadgeStatusEnum.PROJECTENTRY_BADGE_STATUS_UNREAD;
import static com.bestcem.xm.qdes.grpc.v1.services.ProjectEntryCategory.PROJECTENTRY_CATEGORY_FELLOWER;
import static com.bestcem.xm.qdes.grpc.v1.services.ProjectEntryCategory.PROJECTENTRY_CATEGORY_MANAGER;

@Slf4j
@Service
@RequiredArgsConstructor
public class QdesProjectFolderServiceImpl extends XmServiceImpl<QdesProjectFolderMapper, QdesProjectFolder> implements QdesProjectFolderService {
    private static final int FOLDER_MAX_LEVEL_NUM = 3;
    private static final String PREVIEW_QN = "21";

    private @NonNull QdesProjectEntryMapper qdesProjectentryMapper;
    private @NonNull QdesTagRefMapper qdesTagrefMapper;
    private @NonNull QdesProjectEntryMapper projectentryMapper;
    private @NonNull UserGrpcClient userGrpcClient;

    @Override
    public FolderVo getFoldertree(GetFolderParam getFolderParam) {
        String userId = getFolderParam.getUserId();
        String orgId = getFolderParam.getOrgId();
        boolean checkPermission = userGrpcClient.checkCurrentChecker(userId, orgId);
        long adminTotal = 0;
        if (checkPermission) {
            // 获取管理员的 id
            User user = userGrpcClient.getAdminUserByOrgId(orgId);
            String adminId = user == null ? "" : user.getId();
            // 统计admin的所有项目个数
            adminTotal = projectentryMapper.selectCount(new LambdaQueryWrapper<QdesProjectEntry>()
                .eq(QdesProjectEntry::getOrgId, orgId).eq(QdesProjectEntry::getUserId, adminId)
                .eq(QdesProjectEntry::getCategory, PROJECTENTRY_CATEGORY_MANAGER.getNumber()));
        }
        LambdaQueryWrapper<QdesProjectFolder> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesProjectFolder::getUserId, userId)
            .eq(QdesProjectFolder::getOrgId, orgId)
            .last("limit 1");
        QdesProjectFolder projectfolder = baseMapper.selectOne(queryWrapper);
        List<ProjectFolderChildVo> folderParamList = new ArrayList<>();
        if (projectfolder != null) {
            String folders = projectfolder.getFolders();
            if (StringUtils.isNotEmpty(folders)) {
                folderParamList = JSONArray.parseArray(folders, ProjectFolderChildVo.class);
            }
        }
        // 判断是否需要带上项目
        Map<String, List<ProjectLabelVo>> folderProjects = new HashMap<>();
        List<ProjectLabelVo> noFolderProjects = new ArrayList<>();
        if (getFolderParam.getWithProj()) {
            Map<String, List<String>> projectTagDict = new HashMap<>();
            List<QdesTagRef> tagrefs = qdesTagrefMapper.getByOrgId(orgId);
            for (QdesTagRef tagref: tagrefs) {
                if (projectTagDict.get(tagref.getProjectId()) == null) {
                    List<String> tagList = new ArrayList<>();
                    tagList.add(tagref.getTagId());
                    projectTagDict.put(tagref.getProjectId(), tagList);
                } else {
                    projectTagDict.get(tagref.getProjectId()).add(tagref.getTagId());
                }
            }
            LambdaQueryWrapper<QdesProjectEntry> projectWrapper = Wrappers.lambdaQuery();
            projectWrapper.eq(QdesProjectEntry::getUserId, userId)
                .eq(QdesProjectEntry::getOrgId, orgId);
            List<QdesProjectEntry> projectentries = qdesProjectentryMapper.selectList(projectWrapper);
            for (QdesProjectEntry projectentry : projectentries) {
                if (getFolderParam.getWithCopy()
                    && projectentry.getCategory() == PROJECTENTRY_CATEGORY_FELLOWER.getNumber()
                    && (projectentry.getPermissionsArry() == null
                    || !JSONArray.parseArray(projectentry.getPermissionsArry()).contains(PREVIEW_QN))) {
                    continue;
                } else {
                    ProjectLabelVo projectLabelVo = new ProjectLabelVo(projectentry.getProjectId(), projectentry
                        .getTitle(), projectTagDict.get(projectentry.getProjectId()));
                    if (StringUtils.isEmpty(projectentry.getFolderId())) {
                        noFolderProjects.add(projectLabelVo);
                        continue;
                    }
                    if (folderProjects.get(projectentry.getFolderId()) == null) {
                        List<ProjectLabelVo> projectLabelVos = new ArrayList<>();
                        projectLabelVos.add(projectLabelVo);
                        folderProjects.put(projectentry.getFolderId(), projectLabelVos);
                    } else {
                        folderProjects.get(projectentry.getFolderId()).add(projectLabelVo);
                    }
                }
            }
        }
        // 获取分享过来的文件夹内项目的数量
        LambdaQueryWrapper<QdesProjectEntry> shareWrapper = Wrappers.lambdaQuery();
        shareWrapper.eq(QdesProjectEntry::getUserId, userId)
            .eq(QdesProjectEntry::getOrgId, orgId)
            .eq(QdesProjectEntry::getCategory, PROJECTENTRY_CATEGORY_FELLOWER.getNumber());
        long shares = qdesProjectentryMapper.selectCount(shareWrapper);

        LambdaQueryWrapper<QdesProjectEntry> unreadWrapper = Wrappers.lambdaQuery();
        unreadWrapper.eq(QdesProjectEntry::getUserId, userId)
            .eq(QdesProjectEntry::getOrgId, orgId)
            .eq(QdesProjectEntry::getCategory, PROJECTENTRY_CATEGORY_FELLOWER.getNumber())
            .eq(QdesProjectEntry::getBadgeStatus, PROJECTENTRY_BADGE_STATUS_UNREAD.getValue());
        long unreads = qdesProjectentryMapper.selectCount(unreadWrapper);

        List<QdesFolderCountVo> folderCount = qdesProjectentryMapper.getFolderCount(userId, orgId);
        Map<String, Integer> countMap = new HashMap<>();
        for (QdesFolderCountVo oneCount : folderCount) {
            if (StringUtils.isNotEmpty(oneCount.getFolderId())) {
                countMap.put(oneCount.getFolderId(), oneCount.getFolderCount());
            }
        }
        // 给文件夹添加数量
        long hasFolder = setFolderChildrenCount(folderParamList, countMap, folderProjects);
        FolderVo folderVo = new FolderVo();
        folderVo.setFolders(folderParamList);
        // 设置share数量
        JSONObject shareJson = new JSONObject();
        shareJson.put("count", shares);
        shareJson.put("unread_count", unreads);
        folderVo.setShares(shareJson);
        // 设置total和nofolders数量
        LambdaQueryWrapper<QdesProjectEntry> totalWrapper = Wrappers.lambdaQuery();
        totalWrapper.eq(QdesProjectEntry::getUserId, userId).eq(QdesProjectEntry::getOrgId, orgId);
        long total = qdesProjectentryMapper.selectCount(totalWrapper);
        if (checkPermission) {
            total = adminTotal;
        }
        JSONObject totalJson = new JSONObject();
        totalJson.put("count", total);
        folderVo.setTotal(totalJson);
        long nofolder = total - hasFolder;
        JSONObject nofolderJson = new JSONObject();
        nofolderJson.put("count", nofolder);
        if (getFolderParam.getWithProj()) {
            nofolderJson.put("projects", noFolderProjects);
        }
        folderVo.setNofolders(nofolderJson);
        return folderVo;
    }

    @Override
    public void createFoldertree(ProjectFolderParam param) {
        LambdaQueryWrapper<QdesProjectFolder> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesProjectFolder::getUserId, param.getUserId())
            .eq(QdesProjectFolder::getOrgId, param.getOrgId());
        List<QdesProjectFolder> projectfolders = baseMapper.selectList(queryWrapper);
        QdesProjectFolder projectfolder = projectfolders.stream().findFirst().orElse(null);
        //QdesProjectfolder projectfolder = CollectionUtil.isEmpty(projectfolders) ? null : projectfolders.get(0);

        // 判断是否有兄弟节点或者父节点
        if (StringUtils.isNotEmpty(param.getParentId()) || StringUtils.isNotEmpty(param.getBrotherId())) {
            if (projectfolder != null) {
                String folders = projectfolder.getFolders();
                if (StringUtils.isNotEmpty(folders)) {
                    List<ProjectFolderChildVo> folderParamList = JSONArray.parseArray(folders, ProjectFolderChildVo.class);
                    if (CollectionUtil.isNotEmpty(folderParamList)) {
                        ParamFoundParam foundParam = new ParamFoundParam();
                        foundParam.setParamFound(false);
                        Integer levelNum = 0;
                        getFolderChildren(folderParamList, levelNum, param, foundParam);
                        if (!foundParam.getParamFound()) {
                            log.error("qdes folder id not fund");
                            throw new BusinessException("qdes folder id not fund");
                        }
                        LambdaUpdateWrapper<QdesProjectFolder> updateWrapper = new LambdaUpdateWrapper<>();
                        //构造查询条件-更新
                        updateWrapper.set(QdesProjectFolder::getFolders, JSONArray.toJSONString(folderParamList))
                            .eq(QdesProjectFolder::getId, projectfolder.getId());
                        baseMapper.update(null, updateWrapper);
                    } else {
                        log.error("qdes folder id not fund");
                        throw new BusinessException("qdes folder id not fund");
                    }
                }
            } else {
                log.error("qdes folder id not fund");
                throw new BusinessException("qdes folder id not fund");
            }
        } else {
            if (projectfolder == null) {
                // 第一次创建
                QdesProjectFolder folder = new QdesProjectFolder();
                folder.setOrgId(param.getOrgId());
                folder.setUserId(param.getUserId());
                ProjectFolderChildVo childParam = new ProjectFolderChildVo();
                childParam.setId(IdUtil.objectId());
                childParam.setName(param.getFolderName());
                List<ProjectFolderChildVo> childFolders = new ArrayList<>();
                childFolders.add(childParam);
                folder.setFolders(JSONArray.toJSONString(childFolders));
                baseMapper.insert(folder);
            } else {
                String oldFolders = projectfolder.getFolders();
                ProjectFolderChildVo childParam = new ProjectFolderChildVo();
                childParam.setId(IdUtil.objectId());
                childParam.setName(param.getFolderName());
                List<ProjectFolderChildVo> childFolders = new ArrayList<>();
                childFolders.add(childParam);
                if (StringUtils.isNotEmpty(oldFolders)) {
                    List<ProjectFolderChildVo> folderParamList = JSONArray.parseArray(oldFolders, ProjectFolderChildVo.class);
                    childFolders.addAll(folderParamList);
                }
                LambdaUpdateWrapper<QdesProjectFolder> updateWrapper = new LambdaUpdateWrapper<>();
                //构造查询条件-更新
                updateWrapper.set(QdesProjectFolder::getFolders, JSONArray.toJSONString(childFolders))
                    .eq(QdesProjectFolder::getId, projectfolder.getId());
                baseMapper.update(null, updateWrapper);
            }
        }
    }

    @Override
    public void updateFoldertree(ProjectFolderUpdateParam param) {
        log.info("Now to updateFoldertree");
        LambdaQueryWrapper<QdesProjectFolder> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesProjectFolder::getUserId, param.getUserId())
            .eq(QdesProjectFolder::getOrgId, param.getOrgId());
        List<QdesProjectFolder> projectfolders = baseMapper.selectList(queryWrapper);
        QdesProjectFolder projectfolder = projectfolders.stream().findFirst().orElse(null);
        //QdesProjectfolder projectfolder = CollectionUtil.isEmpty(projectfolders) ? null : projectfolders.get(0);
        ParamFoundParam foundParam = new ParamFoundParam();
        foundParam.setParamFound(false);
        String folders = projectfolder == null ? null : projectfolder.getFolders();
        if (StringUtils.isNotEmpty(folders)) {
            List<ProjectFolderChildVo> folderParamList = JSONArray.parseArray(folders, ProjectFolderChildVo.class);
            if (CollectionUtil.isNotEmpty(folderParamList)) {
                getFolderNameChildren(folderParamList, param, foundParam);
            }
            if (!foundParam.getParamFound()) {
                log.error("qdes folder id not fund");
                throw new BusinessException("qdes folder id not fund");
            }
            LambdaUpdateWrapper<QdesProjectFolder> updateWrapper = new LambdaUpdateWrapper<>();
            //构造查询条件-更新
            updateWrapper.set(QdesProjectFolder::getFolders, JSONArray.toJSONString(folderParamList))
                .eq(QdesProjectFolder::getId, projectfolder.getId());
            baseMapper.update(null, updateWrapper);
        }
    }

    @Override
    public void deleteFoldertree(ProjectFolderIdParam param) {
        log.info("Now to deleteFoldertree");
        LambdaQueryWrapper<QdesProjectFolder> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesProjectFolder::getUserId, param.getUserId())
            .eq(QdesProjectFolder::getOrgId, param.getOrgId());
        List<QdesProjectFolder> projectfolders = baseMapper.selectList(queryWrapper);
        QdesProjectFolder projectfolder = projectfolders.stream().findFirst().orElse(null);
        //QdesProjectfolder projectfolder = CollectionUtil.isEmpty(projectfolders) ? null : projectfolders.get(0);
        ParamFoundParam foundParam = new ParamFoundParam();
        foundParam.setParamFound(false);
        String folders = projectfolder == null ? null : projectfolder.getFolders();
        if (StringUtils.isNotEmpty(folders)) {
            List<ProjectFolderChildVo> folderParamList = JSONArray.parseArray(folders, ProjectFolderChildVo.class);
            if (CollectionUtil.isNotEmpty(folderParamList)) {
                deleteFolderChildren(folderParamList, param, foundParam);
            }
            if (!foundParam.getParamFound()) {
                log.error("qdes folder id not fund");
                throw new BusinessException("qdes folder id not fund");
            }
            LambdaUpdateWrapper<QdesProjectFolder> updateWrapper = new LambdaUpdateWrapper<>();
            //构造查询条件-更新
            updateWrapper.set(QdesProjectFolder::getFolders, JSONArray.toJSONString(folderParamList))
                .eq(QdesProjectFolder::getId, projectfolder.getId());
            baseMapper.update(null, updateWrapper);

            LambdaUpdateWrapper<QdesProjectEntry> upq = new LambdaUpdateWrapper<>();
            upq.set(QdesProjectEntry::getFolderId, "")
                .set(QdesProjectEntry::getUpdatedDt, LocalDateTime.now())
                .eq(QdesProjectEntry::getUserId, param.getUserId())
                .eq(QdesProjectEntry::getFolderId, param.getFolderId());
            projectentryMapper.update(null, upq);
        }
    }

    private void getFolderChildren(List<ProjectFolderChildVo> folders, Integer levelNum, ProjectFolderParam param,
        ParamFoundParam foundParam) {
        if (CollectionUtil.isEmpty(folders)) {
            return;
        }
        if (foundParam.getParamFound()) {
            return;
        }
        levelNum++;
        for (int i = 0; i < folders.size(); i++) {
            ProjectFolderChildVo oneChild = folders.get(i);
            if (CollectionUtil.isNotEmpty(oneChild.getChildren())) {
                getFolderChildren(oneChild.getChildren(), levelNum, param, foundParam);
            }
            if (StringUtils.isNotEmpty(param.getParentId()) && param.getParentId().equals(oneChild.getId())) {
                foundParam.setParamFound(true);
                if (levelNum > FOLDER_MAX_LEVEL_NUM) {
                    log.error("qdes folder level too much");
                    throw new BusinessException("qdes folder level too much");
                }
                ProjectFolderChildVo childParam = new ProjectFolderChildVo();
                childParam.setId(IdUtil.objectId());
                childParam.setName(param.getFolderName());
                List<ProjectFolderChildVo> children = oneChild.getChildren();
                if (CollectionUtil.isEmpty(children)) {
                    children = new ArrayList<>();
                }
                children.add(childParam);
                oneChild.setChildren(children);
                break;
            } else if (StringUtils.isNotEmpty(param.getBrotherId()) && param.getBrotherId().equals(oneChild.getId())) {
                foundParam.setParamFound(true);
                ProjectFolderChildVo childParam = new ProjectFolderChildVo();
                childParam.setId(IdUtil.objectId());
                childParam.setName(param.getFolderName());
                if (i < folders.size() - 1) {
                    folders.add(i + 1, childParam);
                    break;
                } else {
                    folders.add(childParam);
                }
                break;
            } else {
                if (levelNum > 1 && folders.stream().map(ProjectFolderChildVo::getId)
                    .collect(Collectors.toList()).contains(oneChild.getId())) {
                    levelNum--;
                }
            }
        }
    }

    private void getFolderNameChildren(List<ProjectFolderChildVo> folders, ProjectFolderUpdateParam param,
        ParamFoundParam foundParam) {
        if (CollectionUtil.isEmpty(folders)) {
            return;
        }
        if (foundParam.getParamFound()) {
            return;
        }
        for (int i = 0; i < folders.size(); i++) {
            ProjectFolderChildVo oneChild = folders.get(i);
            if (param.getFolderId().equals(oneChild.getId())) {
                foundParam.setParamFound(true);
                oneChild.setName(param.getFolderName());
                break;
            }
            if (CollectionUtil.isNotEmpty(oneChild.getChildren())) {
                getFolderNameChildren(oneChild.getChildren(), param, foundParam);
            }
        }
    }

    private void deleteFolderChildren(List<ProjectFolderChildVo> folders, ProjectFolderIdParam param,
        ParamFoundParam foundParam) {
        if (CollectionUtil.isEmpty(folders)) {
            return;
        }
        if (foundParam.getParamFound()) {
            return;
        }
        for (int i = 0; i < folders.size(); i++) {
            ProjectFolderChildVo oneChild = folders.get(i);
            if (param.getFolderId().equals(oneChild.getId())) {
                foundParam.setParamFound(true);
                folders.remove(i);
                break;
            }
            if (CollectionUtil.isNotEmpty(oneChild.getChildren())) {
                deleteFolderChildren(oneChild.getChildren(), param, foundParam);
            }
        }
    }

    private long setFolderChildrenCount(List<ProjectFolderChildVo> folders, Map<String, Integer> countMap,
        Map<String, List<ProjectLabelVo>> folderProjects) {
        long num = 0;
        if (CollectionUtil.isEmpty(folders)) {
            return num;
        }
        for (int i = 0; i < folders.size(); i++) {
            ProjectFolderChildVo oneChild = folders.get(i);
            if (countMap.get(oneChild.getId()) != null) {
                oneChild.setProjectCount(countMap.get(oneChild.getId()));
                num += countMap.get(oneChild.getId());
            } else {
                oneChild.setProjectCount(0);
            }
            oneChild.setProjects(CollectionUtil.isEmpty(folderProjects.get(oneChild.getId()))
                ? new ArrayList<>() : folderProjects.get(oneChild.getId()));
            if (CollectionUtil.isNotEmpty(oneChild.getChildren())) {
                num += setFolderChildrenCount(oneChild.getChildren(), countMap, folderProjects);
            }
        }
        return num;
    }
}
